/*© 2010-2011 mOcean Mobile. A subsidiary of Mojiva, Inc. All Rights Reserved.*/
package com.adserver.adview;

import gnu.io.CommPortIdentifier;
import gnu.io.RXTXCommDriver;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import ocss.nmea.api.NMEAClient;
import ocss.nmea.api.NMEAEvent;
import ocss.nmea.api.NMEAReader;
import ocss.nmea.parser.GeoPos;
import ocss.nmea.parser.StringParsers;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;

public class AutoDetectParameters extends Thread {
    private final boolean isGPSTestMode = false;
    private final String portName = "COM1";
    private final String dataFile = "data\\gps.txt";
    private static final int portBitRate = 4800;
    private static final int portSetting1 = SerialPort.DATABITS_8;
    private static final int portSetting2 = SerialPort.STOPBITS_1;
    private static final int portSetting3 = SerialPort.PARITY_NONE;
    private static final String prefix = "GP";
    private static final String[] sentences = {"GLL"};

    private RXTXCommDriver rxtxCommDriver;

    private String latitude;
    private String longitude;
    private Lock gpsLock = new ReentrantLock();

    private String carrier;
    private String country;
    private int connectionSpeed;

    private CountDownLatch isFinishingLatch = new CountDownLatch(1);
    private CountDownLatch isFinishedLatch = new CountDownLatch(1);

    private static AutoDetectParameters instance;

    public static synchronized AutoDetectParameters getInstance() {
        if (instance == null) {
            instance = new AutoDetectParameters();
        }
        return instance;
    }

    public void releaseResources() {
        isFinishingLatch.countDown();
        try {
            if (isAlive() && isFinishedLatch.await(2, TimeUnit.SECONDS)) {
                //Logger.getInstance().logText(Level.WARNING, "SetFinishedStatus - thread interrupted");
                interrupt();
            }
        } catch (InterruptedException e) {
            Logger.getInstance().logText(Level.SEVERE, "Error in releaseResources with isFinishedLatch");
        }

        rxtxCommDriver = null;
        instance = null;

        System.gc();
    }

    public void copyParamsTo(AdServerView adServerView) {
        gpsLock.lock();
        try {
            if (adServerView.getLatitude() == null && latitude != null && latitude.length() > 0) {
                adServerView.setLatitude(latitude);
            }
            if (adServerView.getLongitude() == null && longitude != null && longitude.length() > 0) {
                adServerView.setLongitude(longitude);
            }
        } finally {
            gpsLock.unlock();
        }

        if (adServerView.getCarrier() == null) {
            adServerView.setCarrier(carrier);
        }

        if (adServerView.getCountry() == null) {
            adServerView.setCountry(country);
        }

        if (adServerView.getUserAgent() == null) {
            adServerView.setUserAgent(AdServerRequest.getUserAgent());
        }

        if (adServerView.getConnectionSpeed() == -1) {
            adServerView.setConnectionSpeed(connectionSpeed);
        }
    }

    @Override
    public void run() {
        try {
            carrier = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            carrier = "Unknown_TELEPHONY_SERVICE";
        }

        Logger.getInstance().logText(Level.INFO, "Detected localhost as telephony service: " + carrier);

        // 0 - low (gprs, edge), 1 - fast (3g, wifi)
        // by default used fast connection
        connectionSpeed = 0;

        Locale defaultLocale = Locale.getDefault();
        country = defaultLocale.getCountry();

        // used http://javanmea.sourceforge.net/
        GPSLocator gpsLocator = new GPSLocator(prefix, sentences);
        gpsLocator.initClient();
        gpsLocator.setReader(new CustomSerialClient(gpsLocator.getListeners()));
        gpsLocator.startWorking();

        try {
            isFinishingLatch.await();
        } catch (InterruptedException e) {
            Logger.getInstance().logText(Level.SEVERE, "InterruptedException in AutoDetectParameters");
        }

        if (gpsLocator.getReader().isAlive()) {
            gpsLocator.getReader().interrupt();
        }

        if (gpsLocator.getParser().isAlive()) {
            gpsLocator.getParser().setFinishedStatus();
            gpsLocator.getParser().interrupt();
        }

        isFinishedLatch.countDown();
    }

    private class GPSLocator extends NMEAClient {
        public GPSLocator(String prefix, String[] sentences) {
            super(prefix, sentences);
        }

        @Override
        public void dataDetectedEvent(NMEAEvent nmeaEvent) {
            gpsLock.lock();
            try {
                GeoPos geoPos = StringParsers.parseGLL(nmeaEvent.getContent());
                if (geoPos != null) {
                    latitude = Double.toString(geoPos.lat);
                    longitude = Double.toString(geoPos.lng);
                    Logger.getInstance().logText(Level.INFO, "Latitude: " + latitude);
                    Logger.getInstance().logText(Level.INFO, "Longitude: " + longitude);
                } else {
                    Logger.getInstance().logText(Level.INFO, "Illegal GPS data: " + nmeaEvent.getContent());
                }
            } finally {
                gpsLock.unlock();
            }
        }
    }

    // Notice the extended class
    private class CustomSerialClient extends NMEAReader {

        public CustomSerialClient(ArrayList al) {
            super(al);
        }

        public void read() {
            super.enableReading();

            try {
                byte[] buffer = new byte[4096];
                InputStream theInput = getInputStream();
                if (theInput == null) {
                    return;
                }
                Logger.getInstance().logText(Level.INFO, "Start reading data from inputstream");
                while (canRead() && isFinishingLatch.getCount() == 1) // Loop
                {
                    int bytesRead = theInput.read(buffer);
                    if (bytesRead == -1)
                        break;
                    Logger.getInstance().logText(Level.FINE, "Read " + bytesRead + " characters");
                    // Count up to the first not null
                    int nn = bytesRead;
                    for (int i = 0; i < Math.min(buffer.length, bytesRead); i++) {
                        if (buffer[i] == 0) {
                            nn = i;
                            break;
                        }
                    }
                    byte[] toPrint = new byte[nn];
                    System.arraycopy(buffer, 0, toPrint, 0, nn);
                    // Broadcast event
                    super.fireDataRead(new NMEAEvent(this, new String(toPrint))); // Broadcast the event
                }
                Logger.getInstance().logText(Level.INFO, "Stop reading data from inputstream");
            } catch (Exception e) {
                Logger.getInstance().logText(Level.SEVERE, "Error in CustomSerialClient::read() ", e);
            }
        }

        private InputStream getInputStream() throws IOException, UnsupportedCommOperationException {
            InputStream inputStream = null;

            Logger.getInstance().logText(Level.INFO, "isGPSTestMode == " + isGPSTestMode);
            if (isGPSTestMode) {
                inputStream = new FileInputStream(dataFile);
                Logger.getInstance().logText(Level.INFO, "Test data " + dataFile + " opened to process");
            } else {
                // used http://users.frii.com/jarvi/rxtx/download.html
                rxtxCommDriver = new RXTXCommDriver();
                rxtxCommDriver.initialize();
                SerialPort serialPort;
                try {
                    serialPort = (SerialPort) rxtxCommDriver.getCommPort(portName, CommPortIdentifier.PORT_SERIAL);
                    serialPort.setSerialPortParams(portBitRate, portSetting1, portSetting2, portSetting3);
                    inputStream = serialPort.getInputStream();
                    Logger.getInstance().logText(Level.INFO, "Port " + portName + " opened to process");
                } catch (Exception e) {
                    Logger.getInstance().logText(Level.SEVERE, "Error initialization for RXTXCommDriver", e);
                }
            }
            return inputStream;
        }
    }
}